#pragma once
#include "ComponentsPrereq.h"
#include "PhysicsPrereq.h"
#include "Bullet\BulletDynamics\Character\btCharacterControllerInterface.h"
#include "Bullet\BulletCollision\CollisionDispatch\btGhostObject.h"
#include "Bullet\BulletDynamics\Character\btKinematicCharacterController.h"
/*
Creating a rigid body enables the body to become dynamic, static or kinematic. To add a shape simply add a ColliderComponent.
As the actual rigidbody variable pointer is in the game object this class is simply here to control the rigid body from script or other sources
*/
namespace ne{
class PhysicsWorld;
class RigidBodyComponent : public Component,public IComponentObserver,public IComponentCollision {

public:
	RigidBodyComponent();
	~RigidBodyComponent();

	void Init();
	void Shut();
	void SetMass(float mass);
	float GetMass() const;
	btRigidBody* GetRigidBody() const {return m_rigidBody;}
	btCollisionObject* GetCollisionObject(){return static_cast<btCollisionObject*>(m_rigidBody);}
	btCompoundShape* GetCompoundShape() {return m_compoundShape;}
	luabind::object GetAsLua(lua_State* L) {return luabind::object(L,this);}
	void Notify(CEvent* evt);
	void ApplyImpulse(const Ogre::Vector3& impulse, const Ogre::Vector3& relPos);
	void ApplyImpulse(const Ogre::Vector3& impulse);	// Applies the impulse to the central point of the object
	
	void SetFriction(float friction);
	void SetRestitution(float restitution);
	float GetRestitution()const;
	float GetFriction()const;
	const Ogre::Vector3 GetGravity()const;
	void SetGravity(float x,float y,float z);
	void SetGravity(const Ogre::Vector3& gravity);
	void SetInContact(bool value){m_isInContact=value;}
	bool IsInContact()const{return m_isInContact;}
	bool IsStatic();
	void SetStatic(bool p_value);

private:
	static float			s_defaultGravity;
	bool					m_isInContact;
	float					m_mass;
	btRigidBody*			m_rigidBody;
	btCompoundShape*		m_compoundShape;
	PhysicsWorld*			m_physicsWorld;
	CollisionData*			m_collisionData;
};

class CharacterController : public btKinematicCharacterController{
public:
	CharacterController(Ogre::SceneNode* node,btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight,int upAxis=1);
	~CharacterController(void);
	void updateAction(btCollisionWorld* collisionWorld,btScalar dt);
	Ogre::Vector3				m_offset;
protected:
	Ogre::SceneNode* m_node;
};

class CharacterControllerComponent : public Component,public IComponentObserver,public IComponentCollision,public IComponentLateUpdate{
public:
	CharacterControllerComponent();
	~CharacterControllerComponent();
	
	luabind::object GetAsLua(lua_State* L) {return luabind::object(L,this);}
	void Init();
	void Create(float radiust,float halfHeight);
	void Create();
	void Shut();
	void Notify(CEvent* evt);
	void SetWalkDirection(float x,float y,float z);
	void SetWalkDirection(const Ogre::Vector3& dir);
	void SetGravity(float gravity);
	void SetFallSpeed(float fallSpeed);
	void SetMaxSlope(float maxSlope);
	void SetMaxJumpHeight(float maxJumpHeight);
	void SetJumpSpeed(float jumpSpeed);
	void SetShapeOffset(const Ogre::Vector3& p_offset);
	CObjContainer GetCollisionObject(){return CObjContainer((btCollisionObject*)m_ghostObject);}
	void LateUpdate(GameTime* gameTime);

	void SetInContact(bool value){m_isInContact=value;}
	bool IsInContact()const{return m_isInContact;}

	void Jump();
	bool OnGround(){return m_cc->onGround();}
protected:
	void FinalizeController();
	bool								m_isInContact;
	PhysicsWorld*						m_physicsWorld;
	btScalar							m_halfHeight;
	btPairCachingGhostObject*			m_ghostObject;
	btConvexShape*						m_convexShape;
	btConvexShape*						m_standingConvexShape;
	btConvexShape*						m_crouchingConvexShape;
	CollisionData*						m_collisionData;
	btKinematicCharacterController*		m_cc;
	Ogre::Vector3						m_offset;
};
}